<template>
	<view class="yh-dynamic-form-upload" :class="{ 'yh-dynamic-form-upload-disabled': disabled }">
		<view class="header" v-if="['file', 'image', 'video'].includes(mode)">
			<view class="title text-overflow-ellipsis" :class="{ required: ['file', 'image', 'video', 'media'].includes(mode) && required }">
				{{ title }}
			</view>
			<view class="option">
				<uni-file-picker
					v-if="mode === 'file'"
					ref="FileRef"
					v-model="fileList"
					mode="list"
					file-mediatype="all"
					:auto-upload="false"
					:limit="limit"
					:disabled="disabled"
					:file-extname="compFileTypeList"
					@select="selectedFile"
				>
					<template #default>
						<view class="upload-button-box">
							<image :src="Upload" />
						</view>
					</template>
				</uni-file-picker>
				<view class="counter" v-if="['image', 'video'].includes(mode)">{{ fileList.length }} / {{ limit }}</view>
			</view>
		</view>

		<view class="file-list" v-if="['file', 'filePreview'].includes(mode)">
			<view class="item" v-for="item in fileList" :key="item.url">
				<view class="left" @click="filePreview(item)">
					<image :src="Image" v-if="imageExtension.includes(item.type.toLowerCase())" />
					<image :src="Video" v-if="audioExtension.includes(item.type.toLowerCase())" />
					<image :src="Video" v-if="videoExtension.includes(item.type.toLowerCase())" />
					<image :src="Compressed" v-if="compressedExtension.includes(item.type.toLowerCase())" />
					<image :src="PDF" v-if="['pdf'].includes(item.type.toLowerCase())" />
					<image :src="PPT" v-if="['ppt', 'pptx'].includes(item.type.toLowerCase())" />
					<image :src="Word" v-if="['md', 'rtf', 'txt', 'doc', 'docx'].includes(item.type.toLowerCase())" />
					<image :src="Excel" v-if="['xls', 'xlsx'].includes(item.type.toLowerCase())" />
					<view class="info">
						<view class="file-name text-overflow-ellipsis">{{ item.name }}</view>
						<view class="upload-time">{{ item.uploadTime }}</view>
					</view>
				</view>
				<view class="right" @click="fileAction(item)">
					<image :src="DelRed" v-if="mode === 'file'" />
					<image :src="View" v-if="mode === 'filePreview'" />
				</view>
			</view>
		</view>

		<view class="image-grid media-list" v-if="['image', 'imagePreview'].includes(mode)">
			<view class="item item-box" v-for="item in fileList" :key="item.url">
				<image :src="item.fullUrl" mode="aspectFill" @click="filePreview(item)" />
				<view class="close-button" v-if="mode === 'image'" @click="fileAction(item)">
					<view class="close-button-bg">
						<uni-icons type="closeempty" size="13" />
					</view>
				</view>
			</view>
			<view class="file-picker item-box" v-if="fileList.length < limit && mode === 'image'" @click="chooseFile">
				<image :src="UploadImage" />
			</view>
		</view>

		<view class="video-list media-list" v-if="['video', 'videoPreview'].includes(mode)">
			<view class="item" v-for="item in fileList" :key="item.url">
				<video :src="item.fullUrl" />
				<view class="close-button" v-if="mode === 'video'" @click="fileAction(item)">
					<view class="close-button-bg">
						<uni-icons type="closeempty" size="13" />
					</view>
				</view>
			</view>
			<view class="file-picker item-box" v-if="fileList.length < limit && mode === 'video'" @click="chooseFile">
				<image :src="UploadVideo" />
			</view>
		</view>

		<view class="video-preview">
			<video
				id="yh-dynamic-form-upload-video-preview"
				autoplay
				enable-play-gesture
				page-gesture
				vslide-gesture
				:src="videoPreview.url"
				:title="videoPreview.title"
				@loadedmetadata="videoMetaLoaded"
				@fullscreenchange="videoFullscreenChange"
			/>
		</view>
	</view>
</template>

<script>
import { apiGetOssInfo, apiUploadFile, getFileNameInfo } from '@/components/yh-dynamic-form';
import Upload from '../assets/upload.png';
import Image from '../assets/image.png';
import Video from '../assets/video.png';
import Compressed from '../assets/compressed.png';
import PDF from '../assets/pdf.png';
import PPT from '../assets/ppt.png';
import Word from '../assets/word.png';
import Excel from '../assets/excel.png';
import DelRed from '../assets/del-red.png';
import DelBlue from '../assets/del-blue.png';
import View from '../assets/view.png';
import UploadImage from '../assets/upload-image.png';
import UploadVideo from '../assets/upload-video.png';

export default {
	options: {
		styleIsolation: 'shared',
	},
	props: {
		value: {
			type: Array,
			default() {
				return [];
			},
		},
		// 标题
		title: {
			type: String,
			default: '',
		},
		// 组件模式（file文件上传、image图片上传、video视频上传、media媒体上传、filePreview文件预览、imagePreview图片预览、videoPreview视频预览）
		mode: {
			type: String,
			default: 'file',
		},
		// 是否必填（上传模式有效）
		required: {
			type: Boolean,
			default: false,
		},
		// 文件最大上传数量（上传模式有效）
		limit: {
			type: Number,
			default: 5,
		},
		// 文件最大上传大小（上传模式有效，单位M）
		size: {
			type: Number,
			default: 50,
		},
		// 视频最长持续时长（上传模式有效，单位s）
		duration: {
			type: Number,
			default: 30,
		},
		// 最大文件名长度（上传模式有效）
		fileNameLength: {
			type: Number,
			default: 100,
		},
		// 允许上传文件类型（上传模式有效）
		fileTypeList: {
			type: Array,
			default: () => [],
		},
		// 是否禁用
		disabled: {
			type: Boolean,
			default: false,
		},
	},
	data() {
		return {
			Upload,
			Image,
			Video,
			Compressed,
			PDF,
			PPT,
			Word,
			Excel,
			DelRed,
			DelBlue,
			View,
			UploadImage,
			UploadVideo,

			imageExtension: ['jpg', 'jpeg', 'png', 'gif', 'svg'],
			audioExtension: ['mp3', 'wav', 'flac'],
			videoExtension: ['mp4', 'mov', 'm4v', 'mkv', 'avi', 'wmv'],
			mediaExtension: ['mp3', 'wav', 'flac', 'mp4', 'mov', 'm4v', 'mkv', 'avi', 'wmv'],
			compressedExtension: ['7z', 'zip', 'rar'],
			pdfExtension: ['pdf'],
			pptExtension: ['ppt', 'pptx'],
			wordExtension: ['md', 'rtf', 'txt', 'doc', 'docx'],
			excelExtension: ['xls', 'xlsx'],
			documentExtension: ['pdf', 'ppt', 'pptx', 'md', 'rtf', 'txt', 'doc', 'docx', 'xls', 'xlsx'],
			defaultFileTypeList: [
				// 图片
				'jpg',
				'jpeg',
				'png',
				'gif',
				'svg',
				// 音频
				'mp3',
				'wav',
				'flac',
				// 视频
				'mp4',
				'mov',
				'm4v',
				'mkv',
				'avi',
				'wmv',
				// 压缩文件
				'7z',
				'zip',
				'rar',
				// 文档
				'md',
				'rtf',
				'txt',
				'pdf',
				'ppt',
				'pptx',
				'doc',
				'docx',
				'xls',
				'xlsx',
			],

			fileList: [],
			ossInfo: {},
			videoContext: {},
			fileSystemManager: {},
			videoPreview: {
				url: '',
				title: '',
			},
		};
	},
	computed: {
		compFileTypeList() {
			return this?.fileTypeList?.length ? this.fileTypeList : this.defaultFileTypeList;
		},
		imageFileList() {
			return this.fileList.filter((item) => this.imageExtension.includes(item.type));
		},
		audioFileList() {
			return this.fileList.filter((item) => this.audioExtension.includes(item.type));
		},
		videoFileList() {
			return this.fileList.filter((item) => this.videoExtension.includes(item.type));
		},
		mediaFileList() {
			return this.fileList.filter((item) => this.mediaExtension.includes(item.type));
		},
		compressedFileList() {
			return this.fileList.filter((item) => this.compressedExtension.includes(item.type));
		},
		pdfFileList() {
			return this.fileList.filter((item) => this.pdfExtension.includes(item.type));
		},
		pptFileList() {
			return this.fileList.filter((item) => this.pptExtension.includes(item.type));
		},
		wordFileList() {
			return this.fileList.filter((item) => this.wordExtension.includes(item.type));
		},
		excelFileList() {
			return this.fileList.filter((item) => this.excelExtension.includes(item.type));
		},
	},
	watch: {
		value: {
			deep: true,
			immediate: true,
			handler(val) {
				this.fileList = val;
			},
		},
		fileList: {
			deep: true,
			handler(val) {
				this.$emit('input', val);
			},
		},
	},
	methods: {
		// 文件选中上传
		async selectedFile(event) {
			if (!event?.tempFiles?.length && event?.failedCount) return uni.showToast({ title: '文件已损坏', icon: 'none' });
			if (!event?.tempFiles?.length) return;
			const tempFileList = [];
			let failCount = event?.failedCount || 0;
			event.tempFiles.forEach((item) => {
				if (item.size > this.size * 1024 * 1024) {
					uni.showToast({
						title: `文件大小不能超过${this.size}M`,
						icon: 'none',
					});
				} else if (item.duration > this.duration) {
					uni.showToast({
						title: `视频时长不能超过${this.duration}秒`,
						icon: 'none',
					});
				} else {
					tempFileList.push(item);
				}
			});
			if (!tempFileList.length) return;
			uni.showLoading({
				title: '上传中...',
				mask: true,
			});
			await this.getOssInfo();
			for (const item of tempFileList) {
				if (this.fileList.length >= this.limit) break;
				let fileFullName = item.name;
				if (!fileFullName && item.tempFilePath) {
					const nameStringArray = item.tempFilePath.split('/');
					fileFullName = nameStringArray[nameStringArray.length - 1];
				}
				const nameInfo = getFileNameInfo(fileFullName, this.fileNameLength);
				item.name = nameInfo ? nameInfo[2] : item.name;
				item.fileType = nameInfo ? nameInfo[1] : item.fileType;
				try {
					const res = await apiUploadFile(item, this.ossInfo);
					if (res) {
						this.fileList.push({
							...res,
							name: item.name,
							type: item.fileType,
						});
					} else {
						failCount++;
					}
				} catch (err) {
					failCount++;
				}
			}
			setTimeout(() => uni.hideLoading(), 500);
			if (failCount) {
				uni.showToast({
					title: `${tempFileList.length + (event?.failedCount || 0) - failCount}个文件上传成功，${failCount}个文件上传失败`,
					icon: 'none',
				});
			}
		},
		// 获取阿里云oss信息
		async getOssInfo() {
			try {
				const res = await apiGetOssInfo();
				if (res) {
					this.ossInfo = res;
				} else {
					uni.showToast({
						title: '未获取到oss信息',
						icon: 'none',
					});
				}
			} catch (err) {
				setTimeout(() => uni.hideLoading(), 1500);
			}
		},
		// 文件操作
		fileAction(file) {
			if (this.disabled) return;
			if (['file', 'image', 'video'].includes(this.mode)) {
				this.fileList = this.fileList.filter((item) => item.url !== file.url);
			} else if (this.mode === 'filePreview') {
				this.filePreview(file);
			}
		},
		// 文件预览
		filePreview(file) {
			if (this.disabled) return;
			if (this.imageExtension.includes(file.type)) {
				uni.previewImage({
					urls: this.imageFileList.map((item) => item.fullUrl),
					current: this.imageFileList.findIndex((item) => item.url === file.url),
				});
			} else if (this.videoExtension.includes(file.type)) {
				this.previewVideo(file);
			} else if (this.documentExtension.includes(file.type)) {
				this.previewDocument(file);
			} else {
				uni.showToast({
					title: '该类型文件无法预览',
					icon: 'none',
				});
			}
		},
		// 视频预览
		previewVideo(file) {
			uni.showLoading({
				title: '加载中...',
				mask: true,
			});
			this.videoPreview = {
				url: file.fullUrl,
				title: file.name,
			};
			this.$nextTick(() => {
				this.videoContext?.play();
				this.videoContext?.seek(0);
				setTimeout(() => uni.hideLoading(), 2500);
			});
		},
		// 视频元数据加载完成事件
		videoMetaLoaded() {
			uni.hideLoading();
			this.videoContext?.requestFullScreen();
		},
		// 视频全屏事件
		videoFullscreenChange(event) {
			// 退出全屏
			if (!event.detail.fullscreen) {
				this.videoContext?.pause();
				this.videoPreview = {
					url: '',
					title: '',
				};
			}
		},
		// 文档预览
		previewDocument(file) {
			uni.showLoading({
				title: '加载中...',
				mask: true,
			});
			uni.downloadFile({
				url: file.fullUrl,
				success: (res) => {
					uni.hideLoading();
					const filePath = res.tempFilePath;
					uni.openDocument({
						filePath: filePath,
						showMenu: true,
						fail: () => {
							uni.showToast({
								title: '该类型文件无法预览',
								icon: 'none',
							});
						},
					});
				},
			});
		},
		// 选择文件
		chooseFile() {
			if (this.disabled) return;
			uni.chooseMedia({
				count: this.limit,
				mediaType: this.mode === 'media' ? ['image', 'video'] : [this.mode],
				maxDuration: this.duration,
				success: (res) => {
					this.selectedFile(res);
				},
			});
		},
	},
	created() {
		this.videoContext = uni.createVideoContext('yh-dynamic-form-upload-video-preview', this);
		this.fileSystemManager = uni.getFileSystemManager();
	},
};
</script>

<style scoped lang="scss">
.yh-dynamic-form-upload {
	.header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 30rpx;

		.title {
			color: #6c6d7a;
			font-size: 28rpx;

			&.required {
				&::before {
					content: '*';
					color: #f56c6c;
				}
			}
		}

		.option {
			.upload-button-box {
				width: 65rpx;
				height: 65rpx;
				display: grid;
				place-content: center;
				margin-right: -16rpx;

				image {
					width: 42rpx;
					height: 30rpx;
				}
			}

			/deep/ .uni-file-picker {
				overflow: visible;

				.uni-file-picker__lists {
					display: none;
				}
			}

			.counter {
				color: #989cab;
				font-size: 24rpx;
			}
		}
	}

	.file-list {
		display: flex;
		flex-direction: column;
		gap: 35rpx;

		.item {
			display: flex;
			justify-content: space-between;
			align-items: center;
			gap: 30rpx;

			.left {
				flex: 1;
				display: flex;
				align-items: center;

				image {
					width: 56rpx;
					height: 64rpx;
					margin-right: 20rpx;
				}

				.info {
					flex: 1;

					.file-name {
						color: #4c4e58;
						font-size: 28rpx;
						line-height: 42rpx;
						transform: translateY(-4rpx);
					}

					.upload-time {
						color: #989cab;
						font-size: 22rpx;
						line-height: 30rpx;
					}
				}
			}

			.right {
				width: 65rpx;
				height: 65rpx;
				display: grid;
				place-content: center;
				margin-right: -16rpx;

				image {
					width: 30rpx;
					height: 30rpx;
				}
			}
		}
	}

	.media-list {
		display: flex;
		gap: 20rpx;

		.item {
			position: relative;

			.close-button {
				width: 50rpx;
				height: 50rpx;
				border-radius: 999rpx;
				display: grid;
				place-content: center;
				position: absolute;
				top: -14rpx;
				right: -14rpx;

				.close-button-bg {
					width: 36rpx;
					height: 36rpx;
					text-align: center;
					line-height: 36rpx;
					border-radius: 999rpx;
					overflow: hidden;
					background-color: rgba(18, 23, 47, 0.9);

					/deep/ .uni-icons {
						color: #ffffff !important;
					}
				}
			}
		}

		.item-box {
			width: calc((100% - (20rpx * 2)) / 3);
			aspect-ratio: 1/1;
			border-radius: 8rpx;
		}

		.file-picker {
			display: grid;
			place-content: center;
			background-color: #ffffff;
			border: 2rpx dashed #c9cad7;
			box-sizing: border-box;

			image {
				width: 52rpx;
				height: 46rpx;
				transition: all 0.2s;
			}

			&:active {
				background-color: #f4f5f7;
			}
		}
	}

	.image-grid {
		flex-wrap: wrap;

		.item {
			image {
				display: block;
				width: 100%;
				height: 100%;
				border-radius: 8rpx;
			}
		}
	}

	.video-list {
		flex-direction: column;

		.item {
			video {
				display: block;
				width: 100%;
				height: auto;
				aspect-ratio: 16/9;
				border-radius: 8rpx;
			}
		}
	}

	.video-preview,
	.video-preview video {
		width: 0;
		height: 0;
	}

	&.yh-dynamic-form-upload-disabled .file-picker {
		border-color: #e5e5e5;
		background-color: #f4f5f7;

		&:active {
			background-color: #f4f5f7;
		}
	}
}
</style>
